namespace C4F.DevKit.Bluetooth
{
    namespace ServiceAndDeviceDiscovery
    {
        #region Using Directives
        using System;
        using System.Collections.Generic;
        using System.Text;
        using System.Runtime.InteropServices;
        using System.Net.Sockets;
        using C4F.DevKit.Bluetooth.ServiceAndDeviceDiscovery.Resources;

        #endregion

        /// <summary>
        /// Helper class which imports a number of native APIs which help in 
        /// communicating with the Bluetooth device 
        /// and makes them available for use by other components
        /// </summary>
        public static class BluetoothHelper
        {
            /// <summary>
            /// Constructor for BluetoothHelper class
            /// </summary>
            /// <exception cref="System.Exception">Thrown when winsock 
            /// initialization fails.</exception>            
            static BluetoothHelper()
            {
                // Initialize WinSock
                WsaData wsadata = new WsaData();
                int result = BluetoothHelper.WSAStartup(
                                        BluetoothHelper.MakeWord(2, 2),
                                        ref wsadata);
                if (result != 0)
                    throw new Exception
                        (BluetoothHelperMessages.errorMsgWinsockInit
                           + result.ToString());

                //Initialize a handler for Appdomain unload.
                AppDomain.CurrentDomain.DomainUnload
                    += new EventHandler(CurrentDomain_DomainUnload);
            }

            /// <summary>
            /// Event Handler for AppDomain.DomainUnload event.
            /// </summary>
            /// <param name="sender">The sender of the event.</param>
            /// <param name="e">EventArgs when the current Appdomain 
            /// unloads.</param>                    
            static void CurrentDomain_DomainUnload(object sender,
                                                   EventArgs e)
            {
                //Cleans up the winsock layer
                WSACleanup();
            }
            /// <summary>
            /// Method for checking for Winsock errors.
            /// </summary>            
            /// <exception cref="WinsockException">Thrown when windows socket 
            /// error occurs.</exception>   
            public static void GetError()
            {
                // check for errors
                int errorCode = BluetoothHelper.WSAGetLastError();
                WinsockError winsockError = new WinsockError();
                if (errorCode != 0)
                {
                    switch (errorCode)
                    {
                        case 10004:                           
                            winsockError.ErrorMessage =
                                BluetoothHelperMessages.errorMsg10004;
                            break;
                        case 10013:                            
                            winsockError.ErrorMessage =
                                BluetoothHelperMessages.errorMsg10013;
                            break;
                        case 10014:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10014;
                            break;
                        case 10022:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10022;
                            break;
                        case 10024:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10024;
                            break;
                        case 10035:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10035;
                            break;
                        case 10036:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10036;
                            break;
                        case 10037:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10037;
                            break;
                        case 10038:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10038;
                            break;
                        case 10039:
                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10039;
                            break;
                        case 10040:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10040;
                            break;
                        case 10041:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10041;
                            break;
                        case 10042:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10042;
                            break;
                        case 10043:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10043;
                            break;
                        case 10044:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10044;
                            break;
                        case 10045:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10045;
                            break;
                        case 10046:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10046;
                            break;
                        case 10047:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10047;
                            break;
                        case 10048:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10048;
                            break;
                        case 10049:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10049;
                            break;
                        case 10050:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10050;
                            break;
                        case 10051:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10051;
                            break;
                        case 10052:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10052;
                            break;
                        case 10053:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10053;
                            break;
                        case 10054:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10054;
                            break;
                        case 10055:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10055;
                            break;
                        case 10056:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10056;
                            break;
                        case 10057:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10057;
                            break;
                        case 10058:
                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10058;
                            break;
                        case 10060:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10060;
                            break;
                        case 10061:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10061;
                            break;
                        case 10064:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10064;
                            break;
                        case 10065:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10065;
                            break;
                        case 10067:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10067;
                            break;
                        case 10091:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10091;
                            break;
                        case 10094:                            
                            winsockError.ErrorMessage =
                                 BluetoothHelperMessages.errorMsg10094;
                            break;
                        case 10093:
                            return;

                        case 10101:                            
                            winsockError.ErrorMessage =
                                BluetoothHelperMessages.errorMsg10101;
                            break;
                        case 10102:
                            return;
                        case 10103:                            
                            winsockError.ErrorMessage =
                                BluetoothHelperMessages.errorMsg10103;
                            break;
                        case 10104:                            
                            winsockError.ErrorMessage =
                                BluetoothHelperMessages.errorMsg10104;
                            break;
                        case 10105:                            
                            winsockError.ErrorMessage =
                                BluetoothHelperMessages.errorMsg10105;
                            break;
                        case 10106:                            
                            winsockError.ErrorMessage =
                                BluetoothHelperMessages.errorMsg10106;
                            break;
                        case 10107:                            
                            winsockError.ErrorMessage =
                                BluetoothHelperMessages.errorMsg10107;
                            break;
                        case 10108:
                            return;

                        case 10109:                            
                            winsockError.ErrorMessage =
                                BluetoothHelperMessages.errorMsg10109;
                            break;
                        case 10110:                          
                            return;
                        case 10111:                            
                            winsockError.ErrorMessage =
                                BluetoothHelperMessages.errorMsg10111;
                            break;
                        case 10112:                            
                            winsockError.ErrorMessage =
                                BluetoothHelperMessages.errorMsg10112;
                            break;
                        case 11001:                            
                            winsockError.ErrorMessage =
                                BluetoothHelperMessages.errorMsg11001;
                            break;
                        case 11002:                            
                            winsockError.ErrorMessage =
                                BluetoothHelperMessages.errorMsg11002;                            
                            break;
                        case 11003:                            
                            winsockError.ErrorMessage =
                                BluetoothHelperMessages.errorMsg11003;
                            break;
                        case 11004:                            
                            winsockError.ErrorMessage =
                                BluetoothHelperMessages.errorMsg11004;
                            break;
                        default:
                            return;

                    }
                    winsockError.ErrorCode = errorCode;
                    throw new WinsockException(winsockError);                                       
                }

            }

            #region DLLImports

            /// <summary>
            /// Imports the WSALookupServiceEnd native function.
            /// </summary>
            /// <param name="hLookup">Handle previously obtained by calling 
            /// WSALookupServiceBegin</param>
            /// <returns>The return value is zero if the operation was 
            /// successful. Otherwise, the value SOCKET_ERROR is returned, and
            /// a specific error number can be retrieved by calling 
            /// GetError.</returns>
            
            [DllImport("ws2_32.dll",SetLastError = true,
                EntryPoint = "WSALookupServiceEnd")]
            public static extern int WSALookupServiceEnd(IntPtr hLookup);

             /// <summary>
             /// Imports the WSALookupServiceBegin native function.
             /// </summary>
             /// <param name="restriction">Pointer to the search 
             /// criteria.</param>
             /// <param name="controlFlags">Flag that controls the depth of
             /// the search.</param>
             /// <param name="lookup">Handle to be used when calling 
             /// WSALookupServiceNext in order to start retrieving the results set.</param>
             /// <returns>The return value is zero if the operation was 
             /// successful. Otherwise, the value SOCKET_ERROR is returned, and
             /// a specific error number can be retrieved by calling 
             /// GetError.</returns>
            [DllImport("ws2_32.dll", SetLastError = true, 
            CharSet = CharSet.Auto, EntryPoint = "WSALookupServiceBegin")]
            public static extern int WSALookupServiceBegin(
                                                      QuerySet restriction,    
                                                      uint controlFlags, 
                                                      ref IntPtr lookup);
          
            /// <summary>
            /// Imports the WSALookupServiceNext native function.
            /// </summary>
            /// <param name="lookup">Handle returned from the previous
            /// call to WSALookupServiceBegin.</param>
            /// <param name="controlFlags">Flags to control the next operation. 
            /// </param>
            /// <param name="bufferLength">On input, the number of bytes contained 
            /// in the buffer pointed to by results</param>
            /// <param name="results">Pointer to a block of memory, which 
            /// will contain one result set in a queryset structure on return.
            /// </param>
            /// <returns>The return value is zero if the operation was 
            /// successful. Otherwise, the value SOCKET_ERROR is returned, and
            /// a specific error number can be retrieved by calling 
            /// GetError.</returns>
            [DllImport("ws2_32.dll", SetLastError = true, 
            CharSet = CharSet.Auto, EntryPoint = "WSALookupServiceNext")]
            public static extern int WSALookupServiceNext(
                                                    IntPtr lookup, 
                                                    uint controlFlags, 
                                                    ref int bufferLength, 
                                                    IntPtr results);

            /// <summary>
            /// Imports the WSAStartup native function.
            /// </summary>
            /// <param name="wVersionRequested">Highest version of Windows Sockets 
            /// support that the caller can use.</param>
            /// <param name="wsaData">Pointer to the WSADATA data structure that is 
            /// to receive details of the Windows Sockets implementation</param>
             /// <returns>The return value is zero if the operation was 
            /// successful. Otherwise, the value SOCKET_ERROR is returned, and
            /// a specific error number can be retrieved by calling 
            /// GetError.</returns>
            [DllImport("ws2_32.dll", SetLastError = true, 
            CharSet = CharSet.Auto, EntryPoint = "WSAStartup")]
            public static extern int WSAStartup(short wVersionRequested, 
                                                ref WsaData wsaData);
            /// <summary>
            /// Imports the WSASetService native function.
            /// </summary>
            /// <param name="regInfo">Pointer to the service information for 
            /// registration or deregistration</param>
            /// <param name="operation">Operation to be performed selected
            /// from the WsaSetServiceOp enum.</param>
            /// <param name="flags">Controls scope of operation.</param>
            /// <returns>The return value is zero if the operation was 
            /// successful. Otherwise, the value SOCKET_ERROR is returned, and
            /// a specific error number can be retrieved by calling 
            /// GetError.</returns>
            [DllImport("ws2_32.dll", SetLastError = true, 
            EntryPoint = "WSASetService")]
            public static extern int WSASetService(
                                                QuerySet regInfo, 
                                                WsaSetServiceOp operation, 
                                                int flags);
            
            /// <summary>
            /// Imports the WSAGetLastError native function.
            /// </summary>
            /// <returns>The return value indicates the error code for 
            /// this thread's last Windows Sockets operation that failed.
            /// </returns>
            [DllImport("ws2_32.dll", SetLastError = true, 
              EntryPoint = "WSAGetLastError")]
            public static extern int WSAGetLastError();

            /// <summary>
            /// Imports the WSACleanup native function.
            /// </summary>
             /// <returns>The return value is zero if the operation was 
            /// successful. Otherwise, the value SOCKET_ERROR is returned, and
            /// a specific error number can be retrieved by calling 
            /// GetError.</returns>
            [DllImport("ws2_32.dll", SetLastError = true, 
                EntryPoint = "WSACleanup")]
            public static extern int WSACleanup();
          
            /// <summary>
            ///  Imports the WSAAddressToString native function.
            /// </summary>
            /// <param name="address">Pointer to the sockaddr structure to 
            /// translate into a string</param>
            /// <param name="addressLength">ength of the address in sockaddr, 
            /// in bytes, which may vary in size with different protocols.</param>
            /// <param name="protocolInfo">(Optional) The WSAPROTOCOL_INFO 
            /// structure for a particular provider</param>
            /// <param name="addressString">Buffer that receives the 
            /// human-readable address string.</param>
            /// <param name="addressSize">Returns the length of the string 
            /// actually copied into the buffer.</param>
             /// <returns>The return value is zero if the operation was 
            /// successful. Otherwise, the value SOCKET_ERROR is returned, and
            /// a specific error number can be retrieved by calling 
            /// GetError.</returns>
            [DllImport("ws2_32.dll", SetLastError = true, 
               EntryPoint = "WSAAddressToString")]
            public static extern int WSAAddressToString(
                                            IntPtr address, 
                                            short addressLength, 
                                            WsaProtocolInfo protocolInfo, 
                                            IntPtr addressString, 
                                            ref int addressSize);


            /// <summary>
            /// Imports the BluetoothSdpGetElementData native function.
            /// </summary>
            /// <param name="sdpStream">A pointer to a valid SDP stream</param>
            /// <param name="sdpStreamLength">The length, in bytes, of pSdpStream
            /// .</param>
            /// <param name="data">A pointer to a buffer to be filled with the 
            /// data of the SDP element found at the beginning of the pSdpStream
            /// SDP stream.</param>
             /// <returns>The return value is zero if the operation was 
            /// successful. Otherwise, the value SOCKET_ERROR is returned, and
            /// a specific error number can be retrieved by calling 
            /// GetError.</returns>
            [DllImport("Irprops.cpl", SetLastError = true, 
             CallingConvention = CallingConvention.Cdecl)]
            public static extern int BluetoothSdpGetElementData(
                                             IntPtr sdpStream, 
                                             uint sdpStreamLength, 
                                             out SdpElementData data);

           
             /// <summary>
             /// Imports the BluetoothSdpEnumAttributes native function.
             /// </summary>
             /// <param name="sdpStream">Pointer to a valid record stream that
             /// is formatted as a single SDP record.</param>
             /// <param name="streamSize">Size of the stream pointed to by 
             /// sdpStream, in bytes.</param>
             /// <param name="callbackHandler">Pointer to the callback routine
             /// </param>
             /// <param name="param">Optional parameter to be passed to the 
             /// callback routine.</param>
             /// <returns>The return value is zero if the operation was 
             /// successful. Otherwise, the value SOCKET_ERROR is returned, and
             /// a specific error number can be retrieved by calling 
             /// GetError.</returns>
            [DllImport("Irprops.cpl", SetLastError = true, 
                CallingConvention = CallingConvention.StdCall)]
            public static extern bool BluetoothSdpEnumAttributes(
                                                 IntPtr sdpStream, 
                                                 uint streamSize, 
                                                 CallbackEventHandler callbackHandler, 
                                                 IntPtr param);
           
            #endregion

            #region Constants
            /// <summary>
            /// Represents the socket address length.
            /// </summary>
            public const int SOCKET_ADDRESS_LENGTH = 30;
            /// <summary>
            /// Represents the address offset.
            /// </summary>
            public const int ADDRESS_OFFSET = 2;
            /// <summary>
            /// Represents Guid offset.
            /// </summary>
            public const int GUID_OFFSET = 10;
            /// <summary>
            /// Represents the channel offset.
            /// </summary>
            public const int CHANNEL_OFFSET = 26;
            /// <summary>
            /// Represents the default channel.
            /// </summary>
            public const int ANY_CHANNEL = -1;                     
            /// <summary>
            /// Represents the Bluetooth namespace.
            /// </summary>
            public const int NS_BTH = 16;   
            /// <summary>
            /// Represents the version of SDP.
            /// </summary>                                 
            public const int BTH_SDP_VERSION = 1;         
            /// <summary>
            /// Represents the maximum number of characters for describing the 
            /// Windows Sockets implementation.
            /// </summary>
            public const int WSADESCRIPTION_LEN = 256;
            /// <summary>
            /// Represents the maximum number of characters for describing 
            /// the relevant status or configuration information.
            /// </summary>
            public const int WSASYS_STATUS_LEN = 128;
            /// <summary>
            /// Represents the maximum number of characters for describing a 
            /// protocol.
            /// </summary>
            public const int WSAPROTOCOL_LEN = 255;
            /// <summary>
            /// Represents the Length of protocol chain.
            /// </summary>
            public const int MAX_PROTOCOL_CHAIN = 7;

            /// <summary>
            /// Represents the LUP_DEEP flag
            /// </summary>
            public const int LUP_DEEP = 0x0001;
            /// <summary>
            /// Represents the LUP_CONTAINERS flag
            /// </summary>
            public const int LUP_CONTAINERS = 0x0002;
            /// <summary>
            /// Represents the LUP_RETURN_NAME flag
            /// </summary>
            public const int LUP_RETURN_NAME = 0x0010;
            /// <summary>
            /// Represents the LUP_RETURN_TYPE flag
            /// </summary>
            public const int LUP_RETURN_TYPE = 0x0020;
            /// <summary>
            /// Represents the LUP_RETURN_COMMENT flag
            /// </summary>
            public const int LUP_RETURN_COMMENT = 0x0080;
            /// <summary>
            /// Represents the LUP_RETURN_ADDR flag
            /// </summary>
            public const int LUP_RETURN_ADDR = 0x0100;
            /// <summary>
            /// Represents the LUP_RETURN_BLOB flag
            /// </summary>
            public const int LUP_RETURN_BLOB = 0x0200;
            /// <summary>
            /// Represents the LUP_RETURN_ALL flag
            /// </summary>
            public const int LUP_RETURN_ALL = 0x0FF0;
            /// <summary>
            /// Represents the LUP_RES_SERVICE flag
            /// </summary>
            public const int LUP_RES_SERVICE = 0x8000;
            /// <summary>
            /// Represents the LUP_FLUSHCACHE flag
            /// </summary>
            public const int LUP_FLUSHCACHE = 0x1000;
         
            /// <summary>
            /// Represents the Serial Port Profile Guid
            /// </summary>
            public static Guid SERIAL_PORT_PROFILE = 
                        new Guid("00001101-0000-1000-8000-00805F9B34FB");
            /// <summary>
            /// Represents the Object Push Profile Guid
            /// </summary>
            public static Guid OBJECT_PUSH_PROFILE = 
                        new Guid("00001105-0000-1000-8000-00805F9B34FB");
            /// <summary>
            /// Represents the File Transfer Profile Guid
            /// </summary>
            public static Guid FILE_TRANSFER_PROFILE = 
                        new Guid("00001106-0000-1000-8000-00805F9B34FB");
            /// <summary>
            /// 
            /// Represents the Public Browse Root Guid
            /// </summary>
            public static Guid PUBLIC_BROWSE_UUID = 
                        new Guid("00001002-0000-1000-8000-00805F9B34FB");

            #endregion

            #region Helper Functions

            /// <summary>
            /// Gets the highest version of Windows Sockets support that 
            /// the caller can use.
            /// </summary>
            /// <param name="low">Specifies the major version number.</param>
            /// <param name="high">Specifies the minor version (revision) number.
            /// </param>
            /// <returns>Highest version of windows socket support that the caller
            /// can use.</returns>
            public static short MakeWord(byte low, byte high)
            {
                return ((short)((low) | ((short)(high)) << 8));
            }         
           
            /// <summary>
            /// Delegate used by the BluetoothSdpEnumAttributes function.
            /// </summary>
            /// <param name="attribId">Identifies the current attribute in the
            /// service record.</param>
            /// <param name="valueStream">Represents the value associated with 
            /// the attribute</param>
            /// <param name="streamSize">size in bytes of valueStream</param>
            /// <param name="param">Optional.The context passed in from a previous call 
            /// to the BluetoothSdpEnumAttributes function</param>
            /// <returns>Returns TRUE when the enumeration continues to the next 
            /// attribute identifier found in the stream. Return FALSE
            /// when enumeration of the record attribute identifiers should 
            /// immediately stop.</returns>
            [UnmanagedFunctionPointer(CallingConvention.StdCall, 
               SetLastError = true)]
            public delegate bool CallbackEventHandler(uint attribId,
                                                      IntPtr valueStream, 
                                                      uint streamSize, 
                                                      IntPtr param);
            #endregion
        }
                    
    }
}